"""
PySCeS - Core2
Author: Brett G. Olivier (brettolivier@gmail.com)
Copyright (C) Brett. G. Olivier, Stellenbosch, 2007.
The source code contained in this file is not licensed for use
or redistribution without the author's prior and express permission.
"""

from version import current_version
__version__ = current_version()

import os, copy, time
from getpass import getuser

CurrentDirectory = os.getcwd() # temporary thing

from InfixParser import MyInfixParser
InfixParser = MyInfixParser()
InfixParser.buildlexer()
InfixParser.buildparser(debug=0, debugfile='infix.dbg', tabmodule='infix_tabmodule')
InfixParser.setNameStr('self.', '()')


class CoreToPsc(object):
    core = None
    name = ''
    header_block = ''
    fixed_block = ''
    reaction_block = ''
    species_block = ''
    parameter_block = ''
    arule_block = ''
    notes_block = ''
    SPECIES_CURRENT_VALUE = False
    FIXED_SPECIES_CURRENT_VALUE = False
    PARAMETER_CURRENT_VALUE = False
    
    # future
    compartment_block = ''

    def __init__(self, core):
        self.core = core
        self.name = self.core.name.replace('.psc','')
        self.name = self.core.name.replace('.xml','')

    def setHeader(self, s=''):
        out = '# This PSC file generated by pysXgen (%s)\n# \n# ' % time.strftime("20%y-%m-%y %H:%M")
        ccount = 0
        for c in range(len(s)):
            ccount += 1
            if ccount >= 60 and ccount <=75 and s[c] in [' ', '\t', '\n', '-']:
                out += s[c]+'\n# '
                ccount = 0
            else:
                out += s[c]
        out += '\n# \n# Model: %s\n' % self.core.getName()
        out += ' \n'
        self.header_block = out

    def setFixed(self):
        out = 'FIX: '
        for f in self.core.species:
            if f.fixed:
                out += '%s ' % f.name
        out += '\n \n'
        self.fixed_block = out
        
    def setReactions(self):
        out = ''
        for r in self.core.reactions:
            rout = '\n%s:\n' % r.name
            cnt = 0
            for lh in r.hasSubstrates():
                if cnt != 0:
                    rout += ' + '
                if abs(r.stoichiometry[lh]) == 1.0:
                    rout += '%s' % lh
                else:
                    rout += '{%s}%s' % (abs(r.stoichiometry[lh]), lh)
                cnt += 1
            if r.reversible:
                rout += ' = '
            else:
                rout += ' > '
            cnt = 0
            for rh in r.hasProducts():
                if cnt != 0:
                    rout += ' + '
                if abs(r.stoichiometry[rh]) == 1.0:
                    rout += '%s' % rh
                else:
                    rout += '{%s}%s' % (abs(r.stoichiometry[rh]), rh)
                cnt += 1
            rout += '\n%s\n' % r.formula
            if len(r.hasModifiers()) > 0:
                rout += '# %s has modifier(s): ' % r.name
                for m in r.hasModifiers():
                    rout += '%s ' % m
                rout += ' \n'
            out += rout
        out += ' \n'
        self.reaction_block = out

    def setSpecies(self):
        out = ''
        fixed = ''
        var = ''
        for f in self.core.species:
            if not f.fixed:
                if not self.SPECIES_CURRENT_VALUE:
                    value = f.value_initial
                else:
                    value = f()
                var += '%s = %s\n' % (f.name, value)
            else:
                if not self.FIXED_SPECIES_CURRENT_VALUE:
                    value = f.value_initial
                else:
                    value = f()
                fixed += '%s = %s\n' % (f.name, value)
        out += '# Fixed species\n'
        out += fixed
        out += ' \n'
        out += '# Variable species\n'
        out += var
        out += ' \n'
        self.species_block = out

#TODO:
    def setParameters(self):
        out = ''
        processed = []
        def setP(p2, out2, proced):
            if not self.PARAMETER_CURRENT_VALUE:
                if p2.value_initial != None:
                    value = p2.value_initial
                else:
                    value = p2()
            else:
                value = p2()    
            if p2.name in proced:
                out2 += '# '
            out2 += '%s = %s' % (p2.name, value)
            if not hasattr(p2, 'code_string'):
                if p2.name in proced:
                    out2 += '\t# (initialised)'
                out2 += '\n'
            else:
                if p2.name in proced:
                    out2 += '\t# (initialised)\n'
                else:
                    out2 += '\t# (rule)\n'
            return out2
                
        for r in self.core.reactions:
            out += '# %s parameters\n' % r.name
            for p in r.parameters:
                out = setP(p, out, processed)
                processed.append(p.name)
            out += '\n'
        if len(processed) != len(self.core.hasGlobalParameters()):
            out += '# Additional (global) parameters\n'
            for p in self.core.hasGlobalParameters():
                if p not in processed:
                    p = self.core.get(p)
                    out = setP(p, out, processed)
                    processed.append(p.name)
            out += ' \n'
        self.parameter_block = out

    def setAssignmentRules(self):
        out = ''
        cnt = 0
        for p in self.core.global_parameters:
            if hasattr(p, 'code_string'):
                if cnt == 0:
                    out += '# Assignment rules\n'
                    cnt += 1
                formula = p.code_string.split('=')[1]
                formula = formula.replace('()','')
                formula = formula.replace('numpy.','')
                out += '!F self.%s = %s\n' % (p.name, formula)
        out += ' \n'
        self.arule_block = out

    def setNotes(self, s):
        out = '# Notes\n# '
        ccount = 0
        for c in range(len(s)):
            ccount += 1
            if ccount >= 60 and ccount <=75 and s[c] in [' ', '\t', '\n', '-']:
                out += s[c]+'\n# '
                ccount = 0
            else:
                out += s[c]
        out += ' \n'
        self.notes_block = out

    def write(self, filename=None, directory=None, getF=False):
        if filename == None:
            filename = self.name+'.psc'
        if directory != None:
            assert os.path.exists(directory), '\n%s does not exist.' % directory
            filename = os.path.join(directory, filename)
        print 'Writing file: %s' % filename
        outF = file(filename, 'w')
        outF.write(self.header_block)
        outF.write(self.fixed_block)
        outF.write(self.reaction_block)
        outF.write(self.arule_block)        
        outF.write(self.species_block)
        outF.write(self.parameter_block)
        outF.write(self.notes_block)
        if not getF:
            outF.flush()
            outF.close()
        else:
            return outF
        


class PscToCore(object):
    ModelDir = None
    WorkDir = None

    # old
    ModelFile = None    
    _PysMod__InitStrings = None
    InitParams = None #suspect
    fixed_species = None
    species = None
    parameters = None
    reactions = None
    modifiers = None
    __function_forced_str__ = None
    _Function_time = None
    _Function_user = None
    _Function_init = None
    _PysMod__NetworkDict = None
    
    def __init__(self):
        from pysces.PyscesParse import PySCeSParser
        self.pscParser = PySCeSParser(debug=0)

    def getPSCFileFromDisk(self, ModelFile, ModelDir=None, WorkDir=None):
        """
        find and set up an existing psc file for parsing
        """
        
        if ModelFile[-4:] != '.psc':
            print "Assuming .psc extension"
            ModelFile += '.psc'
        if ModelDir == None:
            # this will probably change in future - bgoli
            ModelDir = CurrentDirectory
        if WorkDir == None:
            WorkDir = CurrentDirectory            
        assert os.path.exists(os.path.join(ModelDir, ModelFile)), \
            '\nFile %s does not exist' % os.path.join(ModelDir, ModelFile)
        setattr(self, 'ModelFile', ModelFile)
        setattr(self, 'ModelDir', ModelDir)
        assert os.path.exists(WorkDir), \
            '\nDirectory %s does not exist' % WorkDir
        setattr(self, 'WorkDir', WorkDir)
    
    def getPSCFileFromString(self, ModelString, WorkDir=None):
        """
        set up a temporary psc file for parsing from model string
        """
        if WorkDir == None:
            WorkDir = CurrentDirectory            
        assert os.path.exists(WorkDir), \
            '\nDirectory %s does not exist' % WorkDir
        setattr(self, 'WorkDir', WorkDir)
        setattr(self, 'ModelDir', WorkDir)
        ModelFile = '%s.psc' % time.time()
        Mfile = file(os.path.join(WorkDir,ModelFile),'w')
        Mfile.write(ModelString)
        Mfile.close()
        setattr(self, 'ModelFile', ModelFile)
        
    
    def getParsedModel(self):
        """
        Parse the input file associated with the PySCeS model instance and assign the basic model attributes
        """        

        self.pscParser.ParsePSC(self.ModelFile,self.ModelDir,self.WorkDir)

        # from _PysMod__NetworkDict
        self._PysMod__InitStrings = [s.replace('self.','') for s in self.pscParser.InitStrings]
        for s in self.pscParser.InitStrings:
            exec(s)
        self.InitParams = [s.replace('self.','') for s in self.pscParser.InitParams]
        self.fixed_species = copy.copy(self.pscParser.fixed_species)
        self.species = copy.copy(self.pscParser.species)
        self.parameters = copy.copy(self.pscParser.parameters)
        self.reactions = copy.copy(self.pscParser.reactions)
        self.modifiers = copy.copy(self.pscParser.modifiers)
        #print self.modifiers
        self._Function_forced = self.pscParser.ForceFunc.replace('self.','')
        self._Function_forced = self.pscParser.ForceFunc.replace('scipy.','')
        self._Function_time = self.pscParser.TimeFunc.replace('self.','')
        self._Function_user = self.pscParser.UserFunc.replace('self.','')
        self._Function_init = self.pscParser.InitFunc.replace('self.','')
        self._PysMod__NetworkDict = copy.copy(self.pscParser.NetworkDict)
        # new

        
    def getModel(self):
        return self

class CoreToSBML(object):
    core = None
    name = None
    SBML = None
    level = 2
    model = None
    document = None

    def __init__(self, core):
        self.core = core
        self.name = self.core.name.replace('.psc','')
        self.name = self.core.name.replace('.xml','')
        if os.sys.platform == 'win32':
            try:
                import pysces.libsbml.libsbmlinit as SBML
                self.SBML = SBML
            except Exception, e:
                print e
                print '\nWindows sbml load error'
                self.SBML = None
        else:
            try:
                import libsbml as SBML
                self.SBML = SBML
            except Exception, e:
                print e
                print 'Posix sbml load error'
                self.SBML = None    

    def createModel(self):
        self.model = self.SBML.Model()
        self.model.setName(self.name)
        self.document = self.SBML.SBMLDocument()
        self.document.setLevel(self.level)

    def setCompartments(self):
        for cs in self.core.compartments:
            comp_def = self.model.createCompartment()
            comp_def.setId(cs[0])
            comp_def.setName(cs[0])
            comp_def.setVolume(float(cs[1]))

    def setDescription(self, txt=None):
        if txt == None:
            txt = self.core.getDescription()
        else:
            self.core.setDescription(txt)            
        notes = '<body xmlns="http://www.w3.org/1999/xhtml">'
        notes += '<span style="font-family: Courier New,Courier,monospace;">'
        notes += txt
        notes += '</span>'
        notes += '</body>'
        self.model.setNotes(notes)

    def setUnits(self, **kwargs):
        #print kwargs
        if len(kwargs.keys()) > 0:
            self.core.setGlobalUnits(**kwargs)
        ud = self.core.getGlobalUnits()
        for un in ud.keys():
            vdef = self.model.createUnitDefinition()
            if un == 'litre': id = 'volume'
            if un == 'mole': id = 'substance'
            if un == 'second': id = 'time'
            vdef.setId(id)
            vdef.setName(id)
            vu = self.model.createUnit()
            vu.setKind(self.SBML.UnitKind_forName(un))
            vu.setMultiplier(ud[un][0])
            vu.setScale(ud[un][1])
            vu.setOffset(0)

    def setSpecies(self):
        for spe in self.core.species:
            s = self.model.createSpecies()
            s.setId(spe.name)
            s.setName(spe.name)
            if spe.compartment not in [p[0] for p in self.core.compartments]:
                print 'warning: defining 1 volume compartment \"%s\"' % spe.compartment
                self.setCompartment(spe.compartment, 1.0)
            s.setCompartment(spe.compartment)            
            if spe.name in self.core.hasFixedSpecies():
                s.setBoundaryCondition(True)
                s.setConstant(True)
            else:
                s.setBoundaryCondition(False)
            s.setInitialConcentration(spe())

    def setParameters(self):
        for par in self.core.global_parameters:
            #print 'Adding parameter:', par.name
            #print type(par)
            
            p = self.model.createParameter()
            p.setId(par.name)
            p.setName(par.name)
            p.setValue(par())
            # first attempt, check for a formula ... could be done with type instead
            if hasattr(par, 'formula'):
                p.setConstant(False)
                r = self.model.createAssignmentRule()
                r.setVariable(par.name)
                r.setFormula(par.formula)
                r.setMathFromFormula()

    def setReactions(self):
        for rxn in self.core.reactions:
            print 'Adding reaction:', rxn.name
            r = self.model.createReaction()
            r.setId(rxn.name)
            r.setName(rxn.name)
            for s in rxn.substrates:
                print '\t' + rxn.name +' has substrate: ' + s.name + ' (%s)' % abs(rxn.stoichiometry[s.name])
                r.addReactant(self.SBML.SpeciesReference(s.name,\
                                abs(rxn.stoichiometry[s.name])))
            for p in rxn.products:
                print '\t' + rxn.name +' has product: ' + p.name + ' (%s)' % abs(rxn.stoichiometry[p.name])
                r.addProduct(self.SBML.SpeciesReference(p.name,\
                                abs(rxn.stoichiometry[p.name])))                
            for m in rxn.modifiers:
                print '\t' + rxn.name +' has modifier: ' + m.name
                self.model.createModifier().setSpecies(m.name)

            formula = rxn.formula.replace('scipy.','')
            if self.core.compartments[0][0] not in self.core.hasGlobalParameters():
                formula = self.core.compartments[0][0] + ' * ( '+ formula +' )'
            r.setKineticLaw(self.SBML.KineticLaw(formula))

            if rxn.reversible:
                r.setReversible(True)
            else:
                r.setReversible(False)

    def getSBMLmodel(self):
        return self.model

    def getSBMLdocument(self):
        self.document.setModel(self.model)
        return self.document
    
    def getSBML(self):
        return self.getSBMLdocument().toSBML()

    def writeSBML2file(self, name, path=None):
        name += '.xml'
        if path == None:
            name = os.path.join(os.getcwd(), name)
        else:
            assert os.path.exists(path), "\n\"%s\" is not a valid path" % path
            name = os.path.join(path, name)
        try: UseR = getuser()
        except: UseR = ''            
        h1 = '<!-- Created with PySCeS ('+ __version__ + ') on ' + time.strftime("%a, %d %b %Y %H:%M:%S") + ' by '+UseR+' -->\n'
        F = file(name, 'w')
        F.write(h1 + self.getSBML())
        F.close()
        print 'Model %s exported as: %s' % (self.name, name)


class SbmlToCore(object):
    SBML = None
    level = 2
    sbml_string = None
    sbml_file = None
    model = None
    document = None
    core = None
    # old
    ModelFile = None
    _PysMod__InitStrings = None
    InitParams = None
    fixed_species = None
    species = None
    parameters = None
    reactions = None
    modifiers = None
    __function_forced_str__ = None
    _Function_time = None
    _Function_user = None
    _Function_init = None
    _PysMod__NetworkDict = None

    def __init__(self):
        if os.sys.platform == 'win32':
            try:
                import pysces.libsbml.libsbmlinit as SBML
                self.SBML = SBML
            except Exception, e:
                print e
                print '\nWindows sbml load error'
                self.SBML = None
        else:
            try:
                import libsbml as SBML
                self.SBML = SBML
            except Exception, e:
                print e
                print 'Posix sbml load error'
                self.SBML = None
        assert self.SBML != None, '\nNo SBML library available'
                
    def getSbmlStringFromDisk(self, sbml, Dir=None):
        if sbml[-4:] != '.xml':
            print "Assuming .xml extension"
            sbml += '.xml'
        if Dir == None:
            Dir = CurrentDirectory
        assert os.path.exists(os.path.join(Dir, sbml)), \
            '\nFile %s does not exist' % os.path.join(Dir, sbml)
        self.sbml_file = os.path.join(Dir, sbml)
        sbmlF = file(self.sbml_file, 'r')
        self.sbml_string = sbmlF.read()
        sbmlF.close()
    
    def getSbmlStringFromString(self, sbml_string):
        self.sbml_string = sbml_string
        self.sbml_file = os.path.join(CurrentDirectory, 'sbml_string_loader.xml')

    def getSbmlModel(self, document=None):
        r = self.SBML.SBMLReader()
        if document == None:
            self.document = r.readSBMLFromString(self.sbml_string)
        else:
            self.document = document
            self.sbml_string = self.document.toSBML()
            self.sbml_file = os.path.join(CurrentDirectory, 'sbml_string_loader.xml')
        self.model = self.document.getModel()

    def getParsedModel(self):
        init_fixed = {}
        init_var = {}
        init_par = {}
        compartments = {}
        a_rules = {}
        
        for i in self.model.getListOfSpecies():
            parName = i.getId()
            if i.getBoundaryCondition() or i.getConstant():
                if i.getConstant() and not i.getBoundaryCondition():
                    print parName, ' is set as constant, assuming: BoundaryCondition = True'
                init_fixed.setdefault(parName, float(i.getInitialConcentration()))
            else:
                init_var.setdefault(parName, float(i.getInitialConcentration()))

        reactions = self.model.getListOfReactions()

        NetworkDict = dict([(i, dict.fromkeys(['Params',
                              'RateEq',
                              'Reagents',
                              'Modifiers',
                              'Type'])) for i in [j.getId() for j in reactions]])

        #print 'init_fixed\n', init_fixed 
        #print 'init_var\n', init_var 
        #print '\nNetwrokDict\n%s\n' % NetworkDict 
        
        for i in reactions:
            rDict = NetworkDict[i.getId()]
            j = i.getKineticLaw()
            par = []
            
            try:
                req = j.getFormula()
                p_names = None
                #if there are local parameters hash them to R_P
                if len(j.getListOfParameters()) > 0:
                    InfixParser.setNameStr('@', '@')
                    InfixParser.parse(req)
                    p_names = InfixParser.names
                    req = InfixParser.output
                    print req
                else:
                    InfixParser.setNameStr('', '')
                    InfixParser.parse(req)
                    p_names = InfixParser.names
                    req = InfixParser.output
                #print p_names
                #print req
                for k in j.getListOfParameters():
                    #TODO: replace with a regular expression based strategy
                    req = req.replace('@'+k.getId()+'@', i.getId() + '_' + k.getId())
                    par.append(i.getId() + '_' + k.getId())
                    init_par.setdefault(i.getId() + '_' + k.getId(), k.getValue())
                    # local parameters are hashed with reaction name
                    print 'Setting local parameter:', i.getId() + '_' + k.getId()
                    ##  # the working way
                    ##  par.append(k.getId())
                    ##  init_par.setdefault(k.getId(), k.getValue())
                rDict['Params'] = par
                req = req.replace('@','')
                rDict['RateEq'] = req
            except Exception, err:
                rDict['Params'] = []
                rDict['RateEq'] = ''
                print err

            Substrates = []
            Products = []

            for k in i.getListOfReactants():
                species = k.getSpecies()
                stoich = -k.getStoichiometry()
                Substrates.append((species,stoich))

            for k in i.getListOfProducts():
                species = k.getSpecies()
                stoich = k.getStoichiometry()
                Products.append((species,stoich))

            # this is to eliminate zero stoichiometries {0}xyz
            badList = []
            for sub in Substrates:
                if sub[1] == 0:
                    badList.append(sub)
            for bad in badList:
                Substrates.pop(Substrates.index(bad))

            badList = []
            for prod in Products:
                if prod[1] == 0:
                    badList.append(prod)
            for bad in badList:
                Products.pop(Products.index(bad))

            # this only makes sense when going to an input file (i think)
            ##  # add source/sink pools to nasty substrate/productless reactions - brett 20050908
            ##  if len(Substrates) == 0:
                ##  Substrates.append(('$pool',-1.0))
            ##  if len(Products) == 0:
                ##  Products.append(('$pool',1.0))

            ##  # print Substrates
            ##  # print Products

            rDict['Reagents'] = dict(Substrates+Products)
            if i.getReversible() == True:
                t = 'Rever'
            else:
                t = 'Irrev'
            rDict['Type'] = t
            NetworkDict[i.getId()].update(rDict)
            
            mods = []
            if len(i.getListOfModifiers()) > 0:
                for m in i.getListOfModifiers():
                    msbml = m.toSBML().split('\"')[1]
                    mods.append(msbml)
            else:
                mods = []
            rDict['Modifiers'] = mods
            #print "Modifiers", rDict['Modifiers']

        #print '\ninit_par\n', init_par  
        #print '\nNetwrokDict\n%s\n' % NetworkDict   

        # Add global parameters
        for x in self.model.getListOfParameters():
            if len(self.model.getListOfParameters()) > 0:
                init_par.setdefault(x.getId(), float(x.getValue()))

        #print '\ninit_par\n', init_par  


        # Initialise compartment volumes as a parameter - brett 20050908
        for comp in self.model.getListOfCompartments():
            compartments.setdefault(comp.getId(), float(comp.getVolume()))

        if len(compartments) > 1:
            print '\nINFO: PySCeS models are currently assumed to have a single compartment'

        if len(compartments) > 0:
            for comp in compartments.keys():
                init_par.setdefault(comp, compartments[comp])

        #print '\ninit_par\n', init_par  
        #print '\ncompartments\n', compartments  
     
        # Add assignment rules as forcing functions - brett 20050908
        for rule in self.model.getListOfRules():
            a_rules.setdefault(rule.getVariable(), rule.getFormula())

        #print '\na_rules\n', a_rules  

        notes = ''
        if len(self.model.getNotes()) > 0:
            notes = self.model.getNotes().replace('\n','\n# ')

        #print "Building core2 attributes\n"
        #print "init_fixed\n", init_fixed
        #print "init_var\n", init_var
        #print "init_par\n", init_par
        #print "compartments\n", compartments
        #print "a_rules\n", a_rules

        # from _PysMod__NetworkDict
        self.ModelFile = self.model.getId()
        if len(self.ModelFile) == 0:
            self.ModelFile = self.model.getName()
            
        InitStrings = []
        for s in init_var.keys():
            line = 'self.' + s + '=' + str(init_var[s])
            InitStrings.append(line)
        for s in init_fixed.keys():
            line = 'self.' + s + '=' + str(init_fixed[s])
            InitStrings.append(line)
        for s in init_par.keys():
            line = 'self.' + s + '=' + str(init_par[s])
            InitStrings.append(line)
            
        self._PysMod__InitStrings = [s.replace('self.','') for s in InitStrings]
        #print 'self._PysMod__InitStrings\n', self._PysMod__InitStrings
        for s in InitStrings:
            exec(s)
        self.InitParams = init_par.keys()
        #print 'self.InitParams\n', self.InitParams
        self.fixed_species = init_fixed.keys()
        #print 'self.fixed_species\n', self.fixed_species
        self.species = init_var.keys()
        #print 'self.species\n', self.species
        self.parameters = init_par.keys()
        #print 'self.parameters\n', self.parameters
        self.reactions = NetworkDict.keys()
        #print 'self.reactions\n', self.reactions
        self.modifiers = []
        for r in NetworkDict.keys():
            self.modifiers.append((r, NetworkDict[r]['Modifiers']))
        #print 'self.modifiers\n', self.modifiers
        if len(a_rules.keys()) > 0:
            self._Function_forced = ''
            for r in a_rules.keys():
                self._Function_forced += r + ' = ' + a_rules[r] + '\n'
        else:
            self._Function_forced = 'pass\n'
        #print 'self._Function_forced\n', self._Function_forced
        self._Function_time = ''
        self._Function_user = ''
        self._Function_init = ''
        self._PysMod__NetworkDict = NetworkDict
        #print 'self._PysMod__NetworkDict\n', self._PysMod__NetworkDict


if __name__ == '__main__':
    from PyscesCore2 import NewCore
    print "\n*****\nTESTING PSC2CORE\n*****\n"
    
    c1 = PscToCore()
    c1.getPSCFileFromDisk('test_bridge.psc', ModelDir='c:\\mypysces\\pscmodels', WorkDir='c:\\temp')
    c1.getParsedModel()
    
    bmap = NewCore(c1)

    print 'Reactions\n', bmap.hasReactions()
    print 'Species\n', bmap.hasSpecies()
    print 'FixedSpecies\n', bmap.hasFixedSpecies()
    print ' '
    print 'R1 has reagents\n', bmap.R1.hasReagents()
    print 'R1 has sub\n', bmap.R1.hasSubstrates()
    print 'R1 has prod\n', bmap.R1.hasProducts()
    print 'R1 has mod\n', bmap.R1.hasModifiers()
    print ' '
    print 's2 is reagent\n', bmap.s2.isReagentOf()
    print 's2 is sub\n', bmap.s2.isSubstrateOf()
    print 's2 is prod\n', bmap.s2.isProductOf()
    print 's2 is mod\n', bmap.s2.isModifierOf()
    print ' '
    print 'R2 stoich\n', bmap.R2.stoichiometry
    print ' '
    print 'findReactionsThatIncludeAllSpecifiedReagents(A, B):', bmap.findReactionsThatIncludeAllSpecifiedReagents('s0','s1')

    print '\nParameter associations'
    for p in bmap.global_parameters:
        print '<\'%s\n%s.isParameterOf() %s' % (str(type(p)).split('.')[-1], p.name, p.isParameterOf())
    
    print '\nbmap Rates'
    for r in bmap.reactions:
        print r.name, '=', r.__call__()

    